package com.symaster.common.test.pid;

import com.symaster.common.swing.FpsManager;
import com.symaster.common.swing.util.SwingUtil;

import javax.swing.*;
import java.awt.*;
import java.util.Arrays;

import static com.symaster.common.test.pid.Util.builderTextField;
import static com.symaster.common.test.pid.Util.time;

/**
 * @author yinmiao
 * @version 2023/11/24
 */
public class App1 {

    public static void main(String[] args) {

        TrainingShed trainingShed = new TrainingShed();
        trainingShed.targetTemperature = 35;

        PIDConfig pidConfig = new PIDConfig();
        pidConfig.p = 0.12f;
        pidConfig.i = 0.00017f;
        pidConfig.d = 0.3f;

        PIDController pidController = new PIDController(pidConfig, trainingShed);
        pidController.start();

        Canvas canvas = new Canvas();
        canvas.trainingShed = trainingShed;

        JPanel jPanel = new JPanel();
        jPanel.setPreferredSize(new Dimension(250, 0));
        jPanel.add(builderTextField("目标温度", trainingShed.targetTemperature + "", (e, t) -> {
            if (e.getKeyCode() == 10) {
                trainingShed.targetTemperature = Float.parseFloat(t.getText());
            }
        }));
        jPanel.add(builderTextField("当前温度", trainingShed.internalTemperature + "", (e, t) -> {
            if (e.getKeyCode() == 10) {
                trainingShed.internalTemperature = Float.parseFloat(t.getText());
            }
        }));
        jPanel.add(builderTextField("P", pidConfig.p + "", (e, t) -> {
            if (e.getKeyCode() == 10) {
                pidConfig.p = Float.parseFloat(t.getText());
            }
        }));
        jPanel.add(builderTextField("I", pidConfig.i + "", (e, t) -> {
            if (e.getKeyCode() == 10) {
                pidConfig.i = Float.parseFloat(t.getText());
            }
        }));
        jPanel.add(builderTextField("D", pidConfig.d + "", (e, t) -> {
            if (e.getKeyCode() == 10) {
                pidConfig.d = Float.parseFloat(t.getText());
            }
        }));

        JPanel jPanel1 = new JPanel();
        jPanel1.setLayout(new BorderLayout());
        jPanel1.add(canvas);
        jPanel1.add(jPanel, BorderLayout.EAST);

        JFrame jFrame = new JFrame("PIDTest");
        jFrame.setContentPane(jPanel1);
        jFrame.setPreferredSize(new Dimension(900, 600));
        jFrame.pack();
        jFrame.setVisible(true);
        SwingUtil.toCenter(jFrame);
    }

    private static class PIDController {
        private final PIDConfig pidConfig;
        private final PID pid;
        private final FpsManager fpsManager;

        public PIDController(PIDConfig pidConfig, PID pid) {
            this.pidConfig = pidConfig;
            this.pid = pid;
            fpsManager = new FpsManager();
        }

        public PIDConfig getPidConfig() {
            return pidConfig;
        }

        public PID getPid() {
            return pid;
        }

        float previousError = 0;
        float integral = 0;

        private double[] integrals = new double[3];

        private Float lastTime;
        public void start() {
            Arrays.fill(integrals, 0.0);

            new Thread(() -> {
                while (true) {

                    if (lastTime != null) {
                        float error = pid.difference();

                        float dt = time() - lastTime;

                        // System.arraycopy(integrals, 1, integrals, 0, integrals.length - 1);
                        // integrals[integrals.length - 1] = error * dt;
                        // float integral = (float) Arrays.stream(integrals).sum();

                        integral = Math.max(Math.min(integral + error * dt, 2000), -2000);

                        float derivative = (error - previousError) / dt;
                        float output = pidConfig.p * error + pidConfig.i * integral + pidConfig.d * derivative;
                        previousError = error;

                        System.arraycopy(integrals, 1, integrals, 0, integrals.length - 1);
                        integrals[integrals.length - 1] = integral;

                        double max = Arrays.stream(integrals).max().orElse(0.0);
                        double min = Arrays.stream(integrals).min().orElse(0.0);
                        System.out.printf("%.6f%n", max - min);

                        // double error = setpoint - actualValue;
                        // integral += error * dt;
                        // double derivative = (error - previousError) / dt;
                        // double output = kp * error + ki * integral + kd * derivative;
                        // previousError = error;
                        // return output;

                        pid.power(output);
                    }

                    lastTime = time();

                    fpsManager.paintWait(2);
                }
            }).start();
        }

        public void stop() {

        }
    }
}
